(***********************************************************************)
(*                                                                     *)
(*  Programowanie Funkcyjne                                            *)
(*  Wydział MiMUW                                                      *)
(*                                                                     *)
(*  Marcin Walas                                                       *)
(*                                                                     *)
(***********************************************************************)
(*
    Moduł udostępniający abstrakcyjne typy dla sztucznej inteligencji
    oraz odpowiednie ich implementacje dla konkretnych gier.
*)

open Printf;;


module type AI_INTERNAL =
    sig
        type board_type (*= CheckersInternal.pieces_type *)
        type move_type (*= CheckersInternal.move_type *)

        (* Legalne wartości są pomiędzy [0..1], czym wyższa waga tym lepszy ruch *)
        val get_weight : board_type -> bool -> float 

        val get_moves : board_type -> bool -> move_type list

        val make_move : board_type -> move_type -> board_type

        val empty_move : move_type
    end;;

module type AI_ABSTRACT =

    functor (AI : AI_INTERNAL) ->
    sig
        type board_type = AI.board_type
        type move_type = AI.move_type

        val make_move : board_type -> bool -> int -> move_type;;
    end;;


module AIAbstract : AI_ABSTRACT = 
    functor (AI : AI_INTERNAL) ->
    struct
        type board_type = AI.board_type;;(* = CheckersInternal.pieces_type;; *)
        type move_type = AI.move_type;;(* = CheckersInternal.move_type;; *)

        let make_move (board:board_type) (red:bool) (max_depth:int) =

            eprintf "AI make move \n";
            flush stderr;

            (* tutaj odbywa się całe alpha-beta itd *)
            let rec recursive_ab (board:board_type) depth =
(*
                eprintf "AI recursion alpha beta at depth %d \n" depth;
                flush stderr;
*)
                let red_cpy = red in
                let red = (if depth mod 2 = 0 then red else not red) in 
                if depth = max_depth then (AI.get_weight board red_cpy), AI.empty_move
                else
                (
                 let moves = AI.get_moves board red in 
                 let extremum = (if depth mod 2 = 0 then 0.0 else 1.0) in
                 let rel x y = if depth mod 2 = 0 then max x y else min x y in
                 let pom acc b =
                    let (w, move) = acc in 
                    let new_w,_ = recursive_ab (AI.make_move board b) (depth+1) in
                    ((rel new_w w)) , (if (rel new_w w) = new_w then b else move)
                
                 in
                 List.fold_left (pom) (extremum, AI.empty_move) moves
                )
           in
           let weight, move = recursive_ab (board) 0 in 
           eprintf "AI move end weight: %f \n" weight;
           flush stderr;
           move;;
    end;;



